package com.ys.fmms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ys.fmms.mapper.PermissionMapper;
import com.ys.fmms.pojo.Permission;
import com.ys.fmms.pojo.RolePermission;
import com.ys.fmms.pojo.UserRole;
import com.ys.fmms.service.PermissionService;
import com.ys.fmms.service.RolePermissionService;
import com.ys.fmms.service.UserRoleService;
import com.ys.fmms.util.JwtHelper;
import com.ys.fmms.util.TimeUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service(value = "permissionServiceImpl")
@Transactional
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {

    @Resource
    private RolePermissionService rolePermissionService;

    @Resource
    private UserRoleService userRoleService;

    // 递归获取全部菜单
    @Override
    public List<Permission> getAllMenuList() {
        // 1.查询菜单表所有数据
        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("id");
        List<Permission> permissionList = baseMapper.selectList(queryWrapper);

        // 2.把查询所有菜单list集合按照要求进行封装
        List<Permission> resultList = buildPermission(permissionList);
        return resultList;
    }

    // 把返回所有菜单list集合进行封装的方法
    public static List<Permission> buildPermission(List<Permission> permissionList) {
        // 创建list集合，用于数据最终封装
        List<Permission> finalNode = new ArrayList<>();
        // 把所有菜单list集合遍历，得到顶层菜单pid=菜单，设置level是1
        for (Permission permissionNode : permissionList) {
            //得到顶层菜单pid=菜单，设置level是1
            if ("0".equals(permissionNode.getPid())) {
                // 设置顶层菜单的level是1
                permissionNode.setLevel(1);
                // 根据顶层菜单，向里面进行查询子菜单，封装到finalNode里面
                finalNode.add(selectChildren(permissionNode, permissionList));
            }
        }
        return finalNode;
    }

    private static Permission selectChildren(Permission permissionNode, List<Permission> permissionList) {
        // 1. 向一层菜单放二层菜单，二层里面放三层，把对象初始化
        permissionNode.setChildren(new ArrayList<>());
        // 2. 遍历所有菜单list集合，进行判断比较，比较id和pid的值是否相同
        for (Permission it : permissionList) {
            // 判断id和pid的值是否相同
            if (permissionNode.getId().equals(it.getPid())) {
                // 把父菜单level+1
                int level = permissionNode.getLevel() + 1;
                it.setLevel(level);
                // 如果children为空，进行初始化操作
                if (permissionNode.getChildren() == null) {
                    permissionNode.setChildren(new ArrayList<>());
                }
                // 把查询出来的子菜单放到父菜单里面
                permissionNode.getChildren().add(selectChildren(it, permissionList));
            }
        }
        return permissionNode;
    }

    // 递归删除菜单
    @Override
    public void removeChildById(String id) {
        // 1.创建list集合，用于封装所有删除菜单id值
        List<String> idList = new ArrayList<>();
        // 2.向idList集合设置删除菜单id
        this.selectPermissionChildById(id, idList);
        // 把当前id封装到list里面
        idList.add(id);
        baseMapper.deleteBatchIds(idList);
    }

    // 3.根据当前菜单id,查询菜单里面子菜单id,封装到list集合
    private void selectPermissionChildById(String id, List<String> idList) {
        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("pid", id);
        queryWrapper.select("id");
        // select id,pid from tb_permission where id=pid
        List<Permission> childIdList = baseMapper.selectList(queryWrapper);
        // 把childIdList里面菜单id值获取出来，封装idList里面，做递归查询
        childIdList.forEach(item -> {
            // 封装idList里面
            idList.add(item.getId());
            // 递归查询
            this.selectPermissionChildById(item.getId(), idList);
        });
    }

    // 给角色分配菜单
    // 按道理的做法是从数据库查询出当前角色对应的菜单然后筛选出数据库中多余的和不存在的分别进行删除和增加
    // 这里为了省事改用通过id删除数据库中对应的菜单，然后在把返回的批量添加
    @Override
    public void saveRolePermissionRelationship(String roleId, String[] permissionIds) {
        // 根据角色Id删除数据库中对应的rolePermission
        QueryWrapper<RolePermission> wrapper = new QueryWrapper<>();
        wrapper.eq("role_id",roleId);
        rolePermissionService.remove(wrapper);
        // permissionId菜单id 数组形式
        // 1.创建list集合，用于封装添加数据
        List<RolePermission> rolePermissionList = new ArrayList<>();

        // 2.遍历所有菜单数组
        for (String perId : permissionIds) {
            //RolePermission对象
            RolePermission rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(perId);
            rolePermission.setCreateTime(TimeUtils.currentDate());
            // 封装到list集合
            rolePermissionList.add(rolePermission);
        }
        // 添加到角色菜单关系表
        rolePermissionService.saveBatch(rolePermissionList);
    }

    // 根据角色获取对应的菜单权限不包括pid=0(即菜单中id为1的项)
    // 最简单的做法就是根据角色id把type=2的id都取出来
    // 这里使用标记的方式对应的角色id在菜单中则改变select为true
    @Override
    public List<Permission> getMenuListByRoleId(String roleId) {
        QueryWrapper<RolePermission> wrapper = new QueryWrapper<>();
        wrapper.eq("role_id", roleId);
        List<RolePermission> rolePermissionList = rolePermissionService.list(wrapper);
        // 获取roleId对应的PermissionIds
        List<String> permissionIdListByRoleId = rolePermissionList
                .stream()
                .map(RolePermission::getPermissionId)
                .collect(Collectors.toList());
        // 获取全部菜单除了id=1并将其转为树形结构进行存储
        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne("id", "1");
        List<Permission> permissionList = baseMapper.selectList(queryWrapper);
        // 根据获取的角色菜单ids给满足其条件的全部菜单改变select属性值
        for (Permission permission : permissionList) {
            if (permissionIdListByRoleId.contains(permission.getId())) {
                permission.setSelect(true);
            }
        }
        // 将菜单转为树形结构
        List<Permission> finalPermissionList = new ArrayList<>();
        permissionList.forEach(item -> {
            if ("1".equals(item.getPid())) {
                item.setLevel(1);
                finalPermissionList.add(buildPermissions(item, permissionList));
            }
        });

        return finalPermissionList;
    }

    private Permission buildPermissions(Permission parItem, List<Permission> permissionList) {
        parItem.setChildren(new ArrayList<>());
        permissionList.forEach(item -> {
            if (parItem.getId().equals(item.getPid())) {
                item.setLevel(parItem.getLevel() + 1);
                parItem.getChildren().add(buildPermissions(item, permissionList));
            }
        });
        return parItem;
    }

    private Boolean queryUserIsNotAdmin(String token) {
        String userId = String.valueOf(JwtHelper.getUserId(token));
        //select from tb_user_role where user_id=userId and role_id=0;
        UserRole admin = userRoleService.getOne(
                new QueryWrapper<UserRole>()
                        .eq("user_id", userId)
                        .eq("role_id", 1));
        if (admin == null) {
            return false;
        }
        return true;
    }

    // 获取用户的菜单和按钮列表
    @Override
    public Map<String, Object> getUserMenuAndButtonList(String token) {
        Map<String, Object> map = new HashMap<>();
        Boolean admin = this.queryUserIsNotAdmin(token);
        if (admin) {
            List<Permission> permissionList = baseMapper.selectList(
                    new QueryWrapper<Permission>()
                            .orderByAsc("id")
            );
            // 根据菜单集合获取type为1的树形菜单
            // 这块按理不应该只返回type为1的菜单，应该返回component的当前用户的所有菜单包括按钮的
            List<Permission> userMenuList = this.getMenuListByType(permissionList);
            map.put("userMenuList", userMenuList);
            // 根据菜单集合获取type为2的list集合
            List<String> buttonList = this.getButtonList(permissionList);
            map.put("buttonList", buttonList);
        } else {
            // 非管理员 根据token获取菜单
            String userId = JwtHelper.getUserId(token);
            List<Permission> userMenuList = baseMapper.getUserMenuList(userId);
            List<Permission> menuListByType = this.getMenuListByType(userMenuList);
            List<String> buttonList = this.getButtonList(userMenuList);
            map.put("userMenuList", menuListByType);
            map.put("buttonList", buttonList);
        }
        return map;
    }

    // 根据type=2 获得按钮权限值列表
    private List<String> getButtonList(List<Permission> permissionList) {
        List<String> permissionValueList = permissionList.stream()
                .filter(item -> "2".equals(item.getType()))
                .map(item -> item.getPermissionValue())
                .collect(Collectors.toList());
        return permissionValueList;
    }

    // 根据type=1 将菜单列表转为树形结构
    private List<Permission> getMenuListByType(List<Permission> permissionList) {
        List<Permission> menuList = new ArrayList<>();
        List<Permission> collectList = permissionList
                .stream()
                .filter(item -> "1".equals(item.getType()))
                .collect(Collectors.toList());
        collectList.forEach(item -> {
            if ("1".equals(item.getPid())) {
                menuList.add(getSelectChildrenNode(item, collectList));
            }
        });
        return menuList;
    }

    private Permission getSelectChildrenNode(Permission childNode, List<Permission> collectList) {
        childNode.setChildren(new ArrayList<>());
        collectList.stream().forEach(item -> {
            if (childNode.getId().equals(item.getPid())) {
                if (childNode.getChildren() == null) {
                    childNode.setChildren(new ArrayList<>());
                }
                childNode.getChildren().add(getSelectChildrenNode(item, collectList));
            }
        });
        return childNode;
    }
}
